package com.ease.script.ognl;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;

import ognl.NullHandler;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import ognl.OgnlRuntime;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.util.reflection.ReflectionException;

/**
 *
 * @author nathanleewei
 *
 */
public class OgnlNullHandler implements NullHandler {

	private static final transient Log log = LogFactory.getLog(OgnlNullHandler.class);

	@SuppressWarnings("rawtypes")
	public Object nullMethodResult(Map context, Object target, String methodName, Object[] args) {

		return null;
	}

	public Object getRealTarget(String property, Map<String, Object> context, Object root) throws OgnlException {
		// special keyword, they must be cutting the stack
		if ("top".equals(property)) {
			return root;
		}

		if (root instanceof CompoundRoot) {
			// find real target
			CompoundRoot cr = (CompoundRoot) root;

			try {
				for (Object target : cr) {
					if (OgnlRuntime.hasSetProperty((OgnlContext) context, target, property) || OgnlRuntime.hasGetProperty((OgnlContext) context, target, property)
							|| OgnlRuntime.getIndexedPropertyType((OgnlContext) context, target.getClass(), property) != OgnlRuntime.INDEXED_PROPERTY_NONE) {
						return target;
					}
				}
			} catch (IntrospectionException ex) {
				throw new ReflectionException("Cannot figure out real target class", ex);
			}

			return null;
		}

		return root;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Object nullPropertyValue(Map context, Object target, Object property) {

		if ((target == null) || (property == null)) {
			return null;
		}

		String propName = property.toString();
		try {
			Object realTarget = getRealTarget(propName, (Map<String, Object>)context, target);

			Class<?> targetClass = realTarget.getClass();

			PropertyDescriptor pd = OgnlRuntime.getPropertyDescriptor(targetClass, propName);

			if (pd == null) {
				return null;
			}

			Class<?> clazz = pd.getPropertyType();

			Object param = createObject(clazz, realTarget, propName, context);

			Ognl.setValue(propName, context, realTarget, param);

			return param;

		} catch (Exception e) {
			log.error("target=" + target + ", property=" + property, e);
		}

		return null;
	}

	@SuppressWarnings("rawtypes")
	private Object createObject(Class<?> clazz, Object target, String property, Map<String, Object> context) throws Exception {
		if (Collection.class.isAssignableFrom(clazz)) {
			return new ArrayList();
		} else if (clazz == Map.class) {
			return new HashMap();
		} else if (clazz == EnumMap.class) {
			throw new RuntimeException("clazz == EnumMap.class");
			// Class keyClass =
			// objectTypeDeterminer.getKeyClass(target.getClass(), property);
			// return new EnumMap(keyClass);
		}

		return clazz.newInstance();
	}

}
